-
It's a technique, not a synchronization primitive.
-
Interesting when you have frequent reads and infrequent writes (e.g., rendering data, physics state).
-
How It Works :
-
Maintain two buffers: a front buffer (for readers) and a back buffer (for writers).
-
Readers always access the front buffer (no locks needed).
-
Writers modify the back buffer, then atomically swap the buffers when done.
-
#include <atomic>
#include <mutex>
struct GameState {
int player_health;
float enemy_positions[1000];
// ... other game data
};
// Double-buffered data
std::atomic<GameState*> front_buffer; // Readers access this (lock-free)
GameState back_buffer; // Writers modify this
std::mutex write_mutex; // Protects back_buffer modifications
// Initialize
void Init() {
front_buffer.store(new GameState(), std::memory_order_relaxed);
}
// Read Job (Lock-Free)
void ReadJob() {
GameState* current = front_buffer.load(std::memory_order_acquire);
// Safely read from 'current' (no locks needed!)
int health = current->player_health;
// ...
}
// Write Job (Synchronized)
void WriteJob() {
std::lock_guard<std::mutex> lock(write_mutex);
// Modify the back buffer
back_buffer.player_health = 100;
// Swap buffers (atomic, readers see the new state immediately)
GameState* old = front_buffer.exchange(&back_buffer, std::memory_order_release);
// Optional: Reuse 'old' for next write to avoid allocations
std::swap(back_buffer, *old);
}
// Cleanup
void Shutdown() {
delete front_buffer.load();
}
-
Scenario: Writer Removes an Element in Double Buffering
-
Initial State:
-
front_bufferβ[A, B, C, D](readers see this) -
back_bufferβ[A, B, C, D](writer modifies this)
-
-
Writer Removes
C:-
Writer modifies
back_bufferβ[A, B, D] -
Writer swaps
frontbufferandbackbuffer(nowfrontbufferpoints toA, B, D).
-
-
Reader Access:
-
Before Swap: A reader sees
A, B, C, D(oldfrontbuffer). -
After Swap: New readers see
A, B, D(updatedfrontbuffer).
-
-
What If a Reader Was Accessing
CWhen It Was Removed?-
If a reader was iterating over
frontbuffer(old buffer:A, B, C, D) while the swap happened, it still seesCbecause:-
The reader holds a pointer/reference to the old
frontbuffer(not affected by the swap). -
The writerβs changes only affect new readers (those accessing
frontbufferafter the swap).
-
-
-
-
Is This Safe?
-
Yes:
-
Readers see a consistent snapshot (old buffer remains valid until they finish).
-
No data races (the swap is atomic, and old buffers arenβt modified).
-
-
BUT there are Memory Leak Risks :
-
If the old
front_bufferis discarded, readers holding references to it may access freed memory. -
Strategy :
-
Manage lifetimes collectively.
-
Only "mark something as dead" without freeing it.
-
The free is done when no one is trying to read.
-
-
-
-